/*
The Official Type of CSGDX-SEC-201701 is SSX1805.
The spi interface is implemented in this header file.
*/

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include "ssx1805.h"
#include "esn.h"
#include "main.h"

/**
  * @brief  spi_set_cs 是否片选
  * @param  fd spi端口
  * 		mode = 0 or 1，拉低或拉高
  * @retval null
  */
static int spi_set_cs(int fd, int mode)
{
	int cs_mode = 0;
	int ret = -1;

	cs_mode = mode;
	if (fd < 0) {
		return FALSE;
	}

	if ((cs_mode == chip_selected) || (cs_mode == chip_release)) {
		ret = ioctl(fd, SPI_IOC_CS_MODE, &cs_mode);
		if (ret < 0) {
			return FALSE;
		}
	}
	else
	{
		return FALSE;
	}

}

static void print_packet_hex(const uint8_t *packet, size_t length) {
    for (size_t i = 0; i < length; i++) {
        printf("%02X ", packet[i]);
        if ((i + 1) % 16 == 0) {
            printf("\n");  // 每行打印 16 个字节
        }
    }
    printf("\n");
}

static int transfer_t(const char    *spidev,
                     unsigned char  *tx,
                     unsigned char  *rx,
                     unsigned int   length,
                     unsigned int   speed_hz,
                     unsigned char  bits_per_word,
                     unsigned short delay_usecs)
{

    struct spi_ioc_transfer spi_transfer;
    int mode;
    int ret;

    memset(&spi_transfer, 0, sizeof(struct spi_ioc_transfer));

    // if (*tx) {
    //     // 打印 _dl_pkt 数组
    //     printf("_dl_pkt:\n");
    //     print_packet_hex(tx, sizeof(dl_pkt_header)+sizeof(dl_pkt_tail));
    // }

    spi_transfer.tx_buf = (unsigned long)tx;
    spi_transfer.rx_buf = (unsigned long)rx;
    spi_transfer.len = length;
    spi_transfer.speed_hz = speed_hz;
    spi_transfer.bits_per_word = bits_per_word;
    spi_transfer.delay_usecs = delay_usecs;

	int fd = open(spidev, O_RDWR);
	if (fd < 0)
	{
        if (errno == EACCES) {
            perror("  Error accessing device");;
        }else{
            fprintf(stderr, "  Failed to open device %s\n", strerror(errno));
        }        
		return FALSE;
	}

	mode = SPI_MODE_3;
	ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
	if (ret < 0)
	{
        close(fd);
        printf("  wr ioctl SPI_MODE_3 failed \n");
		return FALSE;
	}

	spi_set_cs(fd, chip_selected);
    
    ret = ioctl(fd, SPI_IOC_MESSAGE(1), &spi_transfer);
    if (ret < 0) 
    {
        close(fd);
        printf("  trans ioctl message failed \n");
		return FALSE;
    }

	spi_set_cs(fd, chip_release);

	close(fd);

    return TRUE;
}

static int downcheck(const char    *spidev,
                      unsigned char *tx,
                      unsigned char *rx,
                      unsigned int  nTxLen,
                      unsigned int  nRxLen)
{
    int count = 0;
	int i = 0;
    int j = 0;
    int nRet=TRUE;

    uint8_t *  _dl_request[REQPKT_LENGTH];
	//Using a Single Byte to WakeUp the Chip or CleanUp the SPI Bus
	uint8_t chDownLinkWakeUpByte=0x5A;
	uint8_t chUpLinkWakeUpByte=0x5A;

	//Using a Single Byte to Check the Chip Status.
	uint8_t chDownLinkStatusCheckByte=0x5A;
	uint8_t chUpLinkStatusCheckByte=0x00;

    while(count < 10)
	{

		//clean up the data bus
		//Send some single bytes to wake up the 1701 chip
		
		//SPIdev::transfer("/dev/spidev0.0", (uint8_t *)&_dl_pak, (uint8_t *)&_ul_pak, 1);
        nRet = transfer_t(spidev, 
		                 &chDownLinkWakeUpByte, 
		                 &chUpLinkWakeUpByte, 
		                 1,
		                 SPI_SPEED_HZ,
		                 SPI_BIT_PER_WORD,
		                 SPI_DELAY_USECS);
        
        if(nRet == FALSE)
        {
            printf(" tx 0x5A transfer_t failed.	\n");
            return FALSE;
        }

        if ( chUpLinkWakeUpByte == 0x42 || chUpLinkWakeUpByte == 0x52)
		{
           break;

        }else //if(chUpLinkWakeUpByte == BUSY) 
		{
           count ++;
        //    printf(" up %x \n", chUpLinkWakeUpByte);
           usleep(300);
        
        }

    }

	nRet = transfer_t(spidev, 
	                 tx, 
	                 rx, 
	                 (sizeof(dl_pkt_header)+sizeof(dl_pkt_tail) + nTxLen),
	                 SPI_SPEED_HZ,
	                 SPI_BIT_PER_WORD,
	                 SPI_DELAY_USECS);

    if(nRet != TRUE)
    {
        printf(" tx transfer_t failed.	\n");
        return FALSE;
    }
    // if (*rx) {
    //     printf("_ul_pkt:\n");
    //     print_packet_hex(rx, (sizeof(dl_pkt_header)+sizeof(dl_pkt_tail) + nTxLen));
    // }

	while (chUpLinkStatusCheckByte != READY1) 
	{
    	nRet = transfer_t(spidev, 
			             &chDownLinkStatusCheckByte, 
			             &chUpLinkStatusCheckByte, 
			             1,
                         SPI_SPEED_HZ,
                         SPI_BIT_PER_WORD,
                         SPI_DELAY_USECS);

        if(nRet != TRUE)
        {
            printf(" READY1 transfer_t failed.	\n");
            return FALSE;
        }

        j++;
        if (j > 10)
        {
            // printf(" ! READY1  break %x\n", chUpLinkStatusCheckByte);
            break;
        }

    }

    nRet = transfer_t(spidev, 
		             &chDownLinkStatusCheckByte, 
		             &chUpLinkStatusCheckByte, 
		             1,
                     SPI_SPEED_HZ,
                     SPI_BIT_PER_WORD,
                     SPI_DELAY_USECS);

    if(nRet != TRUE)
    {
        printf(" tx READY1 0x5A transfer_t failed.	\n");
        return FALSE;
    }

	if (chUpLinkStatusCheckByte == READY2) 
	{
    	nRet = transfer_t(spidev, 
			             (uint8_t *)&_dl_request, 
			             rx, 
			             (sizeof(ul_pkt_header) + sizeof(ul_pkt_tail) + nRxLen),
                         SPI_SPEED_HZ,
                         SPI_BIT_PER_WORD,
                         SPI_DELAY_USECS);
        
        if(nRet != TRUE)
        {
            printf(" READY2 transfer_t failed.	\n");
            return FALSE;
        }

    }
    else 
	{
        printf(" =READY2 transfer_t failed.	\n");
        return FALSE;
    }

    return TRUE;
}


static int upcheck(unsigned char *rx, unsigned int nRxLen)
{
    //UpLink Packet Header & Tail
    ul_pkt_header   _uph;
    ul_pkt_tail     _upt;

    int i =0;
    memcpy(&_uph, rx, sizeof(ul_pkt_header));
    memcpy(&_upt, &rx[sizeof(ul_pkt_header)+nRxLen], sizeof(ul_pkt_tail));//16+80

    if(_uph.preamble != UL_PREAMBLE) 
    {
        return FALSE;
    }
    
    if(_uph.data_len != nRxLen) 
    {
        return  FALSE;
    }

    if( _uph.stat != 0x9000)
    {
        return FALSE;
    }

    if (_uph.res1 != UL_RESERVED1) 
	{
        return  FALSE;
    }

    return TRUE;
}



static int ssx1805_get_serialnum(char* device, uint8_t* u8_serial_num, unsigned int nLen)
{
    //DownLink Packet Header & Tail
    dl_pkt_header   _dph;
    dl_pkt_tail     _dpt;

    uint8_t         _dl_pkt[PACKET_LENGTH];  
    uint8_t         _ul_pkt[PACKET_LENGTH];

    int nRet;
    
    _dph.preamble   =   DL_PREAMBLE;	//Constant DownLink Packet Preamble 0x43534744
    _dph.data_len   =   nLen;
    _dph.cla        =   CLA_WITHOUR_CRC;	    //Constant CLA 0x80 for situation without CRC32
    _dph.ins        =   0x32;
	_dph.p1         =   0;
    _dph.p2         =   0;
    _dph.res        =   DL_RESERVED;			
    _dpt.tail       =   DL_TAIL;			//Constant DownLink Packet Tail 0x31373031

    //Command Parameter for Readback the Chip Serial Num

    //Form up the Downlink Packet to Get Serial Num of the ssx1805 Chip
    memcpy(&_dl_pkt, &_dph, sizeof(dl_pkt_header));
    memcpy(&_dl_pkt[sizeof(dl_pkt_header)], &_dpt, sizeof(dl_pkt_tail));

    // Transfer formatted downlink packet to ssx1805 and readback the uplink packet
    nRet = downcheck(device,(uint8_t*)_dl_pkt,(uint8_t*)_ul_pkt,0,nLen);
    if(nRet == FALSE)
    {
        printf("  downcheck failed.	\n");
        return FALSE;
    }

    // Check the uplink packet is valid or not.
    nRet = upcheck((uint8_t*)_ul_pkt,nLen);
    if(nRet == FALSE)
    {
        printf("  upcheck failed.	\n");
        return FALSE;
    }

    memcpy(u8_serial_num, &_ul_pkt[16],nLen);
    return nLen;
}

static int  sxx1805_wakeup(const char* device)
{
     char wakeup_dat[128] = {0};
	 int nRet = 0;

	 memset(&wakeup_dat , 0xF, sizeof(wakeup_dat));

     nRet = transfer_t(device, 
                 &wakeup_dat, 
                 NULL, 
                 sizeof(wakeup_dat),
                 SPI_SPEED_HZ,
                 SPI_BIT_PER_WORD,
                 SPI_DELAY_USECS);
    
    if(nRet == FALSE)
    {
        return FALSE;
    }

	return TRUE;
}

/**
  * @brief  将一个十六进制字节串转换成ASCII码表示的十六进制字符串
  * @param  pHex    十六进制数字节串首地址
  * @retval 整数
  */
static void hex_to_str(unsigned char *pHex, unsigned char *pAscii, int nLen)
{
    unsigned char Nibble[2];
    unsigned int i,j;
    for (i = 0; i < nLen; i++){
        Nibble[0] = (pHex[i] & 0xF0) >> 4;
        Nibble[1] = pHex[i] & 0x0F;
        for (j = 0; j < 2; j++){
            if (Nibble[j] < 10){            
                Nibble[j] += 0x30;
            }
            else{
                if (Nibble[j] < 16)
                    Nibble[j] = Nibble[j] - 10 + 'A';
            }
            *pAscii++ = Nibble[j];
        }
    }
}

int get_ssx1805_serial_number(char* u8_serial_str)
{

#ifdef SIM_SN
    // The serial number as a string
    const char* serial_number = "50065348A1531639";
    strcpy(u8_serial_str, serial_number);
    return 16;

#else
    int nRet = -1;
	uint8_t u8_serial_num[32] = {0};

    /*wake up ssx1805 chip*/
    nRet = sxx1805_wakeup(DEV_NAME);
    if(nRet == FALSE ){
		printf("  send wakeup data failed. \n");
    }

	int ret = ssx1805_get_serialnum(DEV_NAME, u8_serial_num, GET_SERIAL_NUM_LEN);
	if (ret > 0)
	{

		hex_to_str(u8_serial_num, u8_serial_str, ret);
	}else
    {
        printf("  get ssx1805 serialnum failed.	\n");
    }


	return ret;
#endif
}


